package com.birdcage.gamemanagers;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import android.util.Log;

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;




///This class will be the base class of all object element with logic in game.
public class GameObject
{
    ///Private member
    public enum EGameObjectType
    {
        eGOT_Player,
        eGOT_NPC,
        eGOT_Monster,
        eGOT_PIE
    }
    
    private boolean m_active = true;    //if it is not active, it won't be updated.
    protected PhysicsElement m_physElem = new PhysicsElement(this); //represent physic info and handler.
    private ArrayList<GraphicsElement> m_renderUnits = new ArrayList<GraphicsElement>();
    private FSM m_fsm = new FSM(this);
    private EGameObjectType m_type;
    private int m_subType;
    private GameObject m_parent;
    private Vector2 m_scale = new Vector2(1, 1);
    private float m_rotation = 0;
      
    public GameObject(EGameObjectType objType, int subType)
    {
        m_type = objType;
        SetSubType(subType);
        GameObjectManager.GetInstance().RegisterGameObject(this);//do this after you give a type and sub - type to this object.
    }
    
    ///Public method
    
    /// after construct, before update.
    public void Init()
    {
    }
    
    public void SetActive(boolean active)
    {
        m_active = active;
    }
    public boolean GetActive()
    {
        boolean result = m_active;
        if (result && m_parent != null)
        {
            result = m_parent.GetActive();
        }
        return result;
    }

    public PhysicsElement GetPhysElem()
    {
        return m_physElem;
    }
    
    public void Update(float deltaTime)
    {        
        if (GetActive())
        {
            try
            {
                GetFSM().Update();
            } catch (IllegalArgumentException e)
            {
                e.printStackTrace();
            } catch (IllegalAccessException e)
            {
                e.printStackTrace();
            } catch (InvocationTargetException e)
            {
                e.printStackTrace();
            }            
            
            m_physElem.Update(deltaTime);
        } 
    }

    public void SetType(EGameObjectType m_type)
    {
        this.m_type = m_type;
    }

    public EGameObjectType GetType()
    {
        return m_type;
    }

    public void SetSubType(int m_subType)
    {
        this.m_subType = m_subType;
    }

    public int GetSubType()
    {
        return m_subType;
    }
    
    public void SetRelativePos(Vector2 pos)
    {
        m_physElem.SetPos(pos);
    }
    
    public void SetWorldPos(Vector2 pos)
    {
        Vector2 delta = new Vector2();
        delta.set(pos);
        if (m_parent != null)
        {            
            delta.sub(m_parent.GetWorldPos());
        }
        
        SetRelativePos(delta);
    }
    
    public void SetWorldPos(float x, float y)
    {
        Vector2 delta = new Vector2();
        delta.set(x,y);
        SetWorldPos(delta);
    }
    
    public Vector2 GetRelativePos()
    {
        return m_physElem.GetRelativePos();
    }
    
    public Vector2 GetWorldPos()
    {
        Vector2 result = GetRelativePos();
        if (m_parent != null)
        {
            result.add(m_parent.GetWorldPos());
        }
        return result;
    }

    public void SetRotation(float m_rotation)
    {
        this.m_rotation = m_rotation;
    }

    public float GetRotation()
    {
        return m_rotation;
    }
    
    public float GetWorldRotation()
    {
        float rotation = m_rotation;
        if (m_parent != null)
        {
            rotation += m_parent.GetWorldRotation();
        }
        return rotation;
    }

    public void SetScale(Vector2 m_scale)
    {
        this.m_scale = m_scale;
    }

    public Vector2 GetScale()
    {
        return m_scale;
    }
    
    public void AddRenderUnit(Texture texture)
    {
        GraphicsElement elem = new GraphicsElement(this, texture);
        m_renderUnits.add(elem);        
    }
    
    public void AddRenderUnit(Texture texture, float originX, float originY)
    {
        m_renderUnits.add(new GraphicsElement(this, texture, originX, originY));
    }
    
    public void AddRenderUnit(Texture texture, Vector2 origin)
    {
        m_renderUnits.add(new GraphicsElement(this, texture, origin));
    }
    
    public ArrayList<GraphicsElement> GetRenderUnit() {
        return m_renderUnits;
    }

    public FSM GetFSM()
    {
        return m_fsm;
    }
    
    //just for fast access.
    public float GetDeltaTime()
    {
        return GameObjectManager.GetInstance().GetDeltaTime();
    }
    
    public void SetPhysicsCallback(Object object, String collideCallback)
    {
        Method enter = null;
        Method update = null;
        Method exit = null;
        try
        {
            Class[] parameterTypes = new Class[1];            
            parameterTypes[0] = PhysicsElement.class;   
            enter = object.getClass().getMethod(collideCallback+"_Enter", parameterTypes);
            update = object.getClass().getMethod(collideCallback+"_Update", parameterTypes);
            exit = object.getClass().getMethod(collideCallback+"_Exit", parameterTypes);
        } catch (SecurityException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // set accessibility true so we can call private function.
        if (enter != null)
        enter.setAccessible(true);
        if (update != null)
        update.setAccessible(true);
        if (exit != null)
        exit.setAccessible(true);
        m_physElem.SetCallBack(enter, update, exit);
    }
}
